﻿// ***********************************************************************
// Provided for Informational Purposes Only
//
// Apache 2.0 License
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY 
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR 
// PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
// ***********************************************************************
// Assembly  : ContractMeow.WinRT
// File      : StringExtensions.cs
//
// ***********************************************************************
using System;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;

namespace ContractMeow.WinRT.Extensions
{
	public static class StringExtensions
	{
		private static readonly Regex MultipleWhitespaceRegex;
		private const String MultipleWhitespacePattern = @"(\ *(?<newline>\r\n|\r|\n)\ *|\ +)";
		private static readonly Regex MatchNewlineRegex;
		private const String MatchNewlinePattern = @"(\r\n|\r|\n)";

		static StringExtensions()
		{
			MultipleWhitespaceRegex = new Regex(MultipleWhitespacePattern, RegexOptions.Multiline);
			MatchNewlineRegex = new Regex(MatchNewlinePattern, RegexOptions.Multiline);
		}

		/// <summary>
		/// Returns <see cref="String.Empty"/> when <paramref name="value"/> is null, otherwise returns <paramref name="value"/>.
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		public static String Coalesce(this Object value)
		{
			if (value == null)
			{
				return String.Empty;
			}
			return value.ToString();
		}

		public static String TruncateText(this String value, int length)
		{
			if (String.IsNullOrWhiteSpace(value))
			{
				return String.Empty;
			}

			var firstLine = value.Trim().Split(Environment.NewLine.ToCharArray()).First();

			if (firstLine.Length < length + 3)
			{
				return firstLine;
			}
			return firstLine.Substring(0, length) + "...";
		}

		public static T ToEnum<T>(this String value) where T : struct
		{
			return (T)Enum.Parse(typeof(T), value);
		}

		public static T ToEnumOrDefault<T>(this String value, T defaultValue) where T : struct
		{
			if (String.IsNullOrEmpty(value))
			{
				return defaultValue;
			}

			// convert railsy values to c#-y values
			if (Char.IsLower(value[0]))
			{
				value = Char.ToUpper(value[0]) + value.Substring(1);
			}

			try
			{
				return ToEnum<T>(value);
			}
			catch
			{
				return defaultValue;
			}
		}

		/// <summary>
		/// Trims leading and trailing whitespaces and replaces multiple sequential spaces with a single space
		/// </summary>
		/// <param name="value">The <see cref="String"/> to normalize.</param>
		/// <returns>A <see cref="String"/> with normalized whitespace.</returns>
		public static String NormalizeSpaces(this String value)
		{
			if (value == null)
			{
				return null;
			}
			if (String.IsNullOrWhiteSpace(value))
			{
				return String.Empty;
			}

			String trimmed = value.Trim();

			return MultipleWhitespaceRegex.Replace(trimmed, match => match.Groups["newline"].Success
																		 ? match.Groups["newline"].Value //newline
																		 : " ");
		}

		/// <summary>
		/// Converts all of the newline characters in a string to unix style (\n) newlines.
		/// </summary>
		/// <param name="value">The <see cref="String"/> to replace the newlines.</param>
		/// <returns>The original string with any newlines replaced with \n.</returns>
		public static String ConvertToUnixNewlines(this String value)
		{
			if (String.IsNullOrEmpty(value))
			{
				return value;
			}

			return ConvertNewlines(value, "\n");
		}

		/// <summary>
		/// Converts all of the newline characters in a string to unix style (\r\n) newlines.
		/// </summary>
		/// <param name="value">The <see cref="String"/> to replace the newlines.</param>
		/// <returns>The original string with any newlines replaced with \r\n.</returns>
		public static String ConvertToWindowsNewlines(this String value)
		{
			if (String.IsNullOrEmpty(value))
			{
				return value;
			}

			return ConvertNewlines(value, "\r\n");
		}

		/// <summary>
		/// Replaces all of the newline characters in a string to the specified replacement.
		/// </summary>
		/// <param name="value">The <see cref="String"/> to replace the newlines.</param>
		/// <param name="replacement">The <see cref="String"/> to use when replacing newlines.</param>
		/// <returns>The original string with any newlines replaced with <paramref name="replacement"/></returns>
		private static String ConvertNewlines(String value, String replacement)
		{
			replacement = replacement ?? String.Empty;
			return MatchNewlineRegex.Replace(value, match => replacement);
		}

		/// <summary>
		/// Converts a <see cref="String"/> to an URL-encoded <see cref="String"/>.
		/// </summary>
		/// <param name="value">The <see cref="String"/> to encode.</param>
		/// <returns>An encoded <see cref="String"/>.</returns>
		public static String UrlEncode(this String value)
		{
			return String.IsNullOrEmpty(value)
					   ? value
					   : WebUtility.UrlEncode(value);
		}

		/// <summary>
		/// Converts a <see cref="String"/> that has been URL-encoded into a decoded <see cref="String"/>.
		/// </summary>
		/// <param name="value">The <see cref="String"/> to decode.</param>
		/// <returns>A decoded <see cref="String"/>.</returns>
		public static String UrlDecode(this String value)
		{
			return String.IsNullOrEmpty(value)
					   ? value
					   : WebUtility.UrlDecode(value);
		}

		/// <summary>
		/// Converts a <see cref="String"/> to an HTML-encoded <see cref="String"/>.
		/// </summary>
		/// <param name="value">The <see cref="String"/> to encode.</param>
		/// <returns>An encoded <see cref="String"/>.</returns>
		public static String HtmlEncode(this String value)
		{
			return String.IsNullOrEmpty(value)
					   ? value
					   : WebUtility.HtmlEncode(value);
		}

		/// <summary>
		/// Converts a <see cref="String"/> that has been HTML-encoded for HTTP transmission into a decoded <see cref="String"/>.
		/// </summary>
		/// <param name="value">The <see cref="String"/> to decode.</param>
		/// <returns>A decoded <see cref="String"/>.</returns>
		public static String HtmlDecode(this String value)
		{
			return String.IsNullOrEmpty(value)
					   ? value
					   : WebUtility.HtmlDecode(value);
		}

		/// <summary>
		/// Determines if a given <see cref="String"/> represents a valid <see cref="Uri"/>
		/// </summary>
		/// <returns>True is the given <see cref="String"/> is a valid Uri of the specified type, otherwise false.</returns>
		public static Boolean IsUri(this String value, UriKind uriKind = UriKind.Absolute)
		{
			return !String.IsNullOrWhiteSpace(value) && Uri.IsWellFormedUriString(value, uriKind);
		}

		/// <summary>
		/// Converts a given <see cref="String"/> into a <see cref="Uri"/>
		/// </summary>
		/// <returns>A <see cref="Uri"/> representation of the given <see cref="String"/> if it was a valid uri, otherwise null.</returns>
		public static Uri ToUri(this String value, UriKind uriKind = UriKind.Absolute)
		{
			if (String.IsNullOrWhiteSpace(value) || !Uri.IsWellFormedUriString(value, uriKind))
			{
				return null;
			}

			return new Uri(value, uriKind);
		}

		/// <summary>
		/// Checks for equality between 2 strings (duh) as efficiently as possible.
		/// </summary>
		/// <param name="value"></param>
		/// <param name="otherValue"></param>
		/// <returns></returns>
		public static Boolean IsEqual(this String value, String otherValue)
		{
			return String.CompareOrdinal(value, otherValue) == 0;
		}
	}
}